home *** CD-ROM | disk | FTP | other *** search
-
- ********* Some notes about handles in MSDOS and Turbo C *********
-
- NOTE: Borland changed the code for handling files in BCC++ 3.0, in a small
- but significant way. Some of the comments below about the way in which
- Turbo C handles files do not apply to BCC++ 3.0. See the file "bcc30.doc"
- for further comments.
-
- (1) MSDOS maintains a table of information about open files. The number
- of entries in this table is set in config.sys by the FILES=nn statement.
- Programs do not refer to this table directly but via a tag called a handle.
- Handles are numbers 0,1,2,...,etc. The number of handles which a program
- can have is limited not just by the number determined by the FILES=nn
- statement but also by the size of a handle table of which each program
- has its own version. By default the handle table is in the PSP of the
- program and can hold 20 entries. Any program can request that it be granted
- a larger table (for DOS versions >= 3.3) by making a call to MSDOS via
- int 0x21, function 0x67.
-
- (2) So far, so good. One of the unfortunate features of MSDOS programs
- is that text files usually use CR-LF character pairs to indicate line
- endings rather than just LF as used in UNIX and C. The MSDOS file system
- cares nothing about this and does not provide any particular support for
- either system.
-
- (3) Turbo C provides support for both systems. In Turbo C, files may
- be either "text" or "binary" files. "Text" files are the special case;
- for files which are opened as "text" files, Turbo C automatically
- translates between the LF which "C" uses and CR-LF character pairs in
- the actual file on disk (or a device).
-
- (4) To support this, EVERY file which Turbo C accesses via its library
- functions (read(), fread(), etc) must be have an associated piece of
- information which says whether it is "text" or "binary". Turbo C keeps
- this information in a table called _openfd.
-
- (5) None of this would necessarily cause any real problems. The problem
- arises because Turbo C uses the DOS file handle as both the "int handle"
- used by its functions AND as the index into the _openfd table. The
- relevant part of _open() function of Turbo C is basically:
- int _openfd[20];
- int _open(char *name, int mode)
- {
- int handle;
- handle = dos_open(name, mode); /* int 21h, function 3dh */
- _openfd[handle] = mode;
- return handle;
- }
- Note that the code does not check whether the handle returned by MSDOS
- is < 20. This will cause no problems to programs which use the default
- handle table but if a program is granted a larger handle table then
- handles with values larger than 19 may be returned and the above code
- will write beyond the boundaries of the _openfd[] array.
-
- (6) Although the Turbo C _open() function does not check the handle,
- the close() function does! You may open() a file which returns a handle
- of value 20 or larger but you can't close() it!
-
- (7) The _openfd array is in a module called FILES2. This is the only
- contents of that module. Therefore the limitation of the size of the
- _openfd array is easily overcome by having a larger array of the
- same name defined in some module which is loaded before the Turbo C
- libraries. Unfortunately this does not overcome the problem with
- close(). However, the close() function is quite simple and writing
- a suitable replacement for it is easy.
-
- (8) I have written some suitable code for Turbo C which appears in the
- file "close.c". See the file "readme" for details on how to use this code.
-
- (9) There is a price to pay for having an increased number of handles.
- The price is:
- (a) in MSDOS: each file appears to require 59 bytes in the internal
- table of MSDOS. On my system, using MSDOS 5.00, an extra 11808
- bytes of memory were used by the resident portion of MSDOS when
- I increased files=55 to files=255.
- (b) the handle table: when a handle table larger than 20 handles is
- requested, MSDOS shifts the handle table out of the PSP. If the
- requested size is N bytes then the new handle table will occupy
- an extra N (rounded up to the nearest multiple of 16) bytes.
- (c) the _openfd[] array: each extra handle increases the size of this
- array by two bytes.
- (d) a few bytes for the increase_handles() function and the call to
- it (about 43+3 = 46 bytes in the small model).
-
-
-
-
- ************ Dangerous curves here (TeX jargon: means beware!) **************
-
- Here is some inside information which I have deduced from observing
- the behaviour of MSDOS 5.00:
-
- For files, there are two tables of interest which I call the
- file_struct_table and the file_table (I don't have a set of MicroSoft
- documentation so I don't know the "official" names, or even if such names
- exist).
-
- The file_struct_table is internal to MSDOS and has nn entries where nn is
- from the files=nn statement in config.sys.
-
- The file_table is for internal use by MSDOS but exists in the memory
- space of your program (in the PSP by default).
-
-
- file_struct_table file_table
- +-------------+ +-----------------+
- | entry #0 | +--- | entry #0 | <----- (handle)
- +-------------+ | +-----------------+
- | entry #1 | <--------+ | entry #1 |
- +-------------+ +-----------------+
- . . . .
- . . . .
- +-------------+ +-----------------+
- | entry #nn-1 | | entry #MAX_FD-1 |
- +-------------+ +-----------------+
- each ~ 59 bytes each = 1 byte
-
-
- Each file_table entry is one byte in size and contains an unsigned
- number in the range [0..255]. The value 255 means that that file_table
- entry is invalid. All other values are pointers to the file_struct_table.
- A file_table entry with value n points to entry #n of the file_struct_table.
- Therefore the file_struct_table is limited in size to 255 usable entries
- (entry #0 to entry #254).
-
- Entry #0, #1, and #2 of the file_struct_table appear to be special and refer
- to the serial device (AUX), console (CON), and printer (PRN) respectively.
- The first five entries of the file_table a program are therefore normally:
- (handle) (file) (value) (purpose)
- 0 CON 1 stdin
- 1 CON 1 stdout
- 2 CON 1 stderr
- 3 AUX 0 stdaux
- 4 PRN 2 stdprn
- If re-direction is used then these will be modified. For example a program
- test2.exe run with test2 >blogs will start with a file_table:
- (handle) (file) (value) (purpose)
- 0 CON 1 stdin
- 1 BLOGS 3 stdout
- 2 CON 1 stderr
- 3 AUX 0 stdaux
- 4 PRN 2 stdprn
-
- Now for the interesting part...
-
- Notice that five entries of the file_table may be used to refer to just
- three entries of the file_struct_table.
-
- Every time a program opens another file, both an unused entry in the
- file_struct_table and an unused entry in the file_table are allocated
- (even if the same file is opened over and over..). If files=255 is specified
- in config.sys then there are up to 255-3 = 252 entries available in the
- file_struct_table for use in response to requests to open files. This then
- implies that the file_table can be up to 252+5=257 entries long.
-
- What is a handle? A handle is just an entry number of (i.e. an index into)
- the file_table. Since the file_table can have up to 257 entries, a handle
- can have values in the range [0..256]. The important point here is that if
- you want to use the maximum possible number of file handles then IT IS
- INVALID TO USE A BYTE TO STORE A FILE HANDLE, a larger unit of storage is
- required e.g. a short or int in "C".
-
- If you want to be able to have the maximum possible number of file handles
- under MSDOS 3.1 (3.0 ?) or greater then:
- (a) put files=255 in config.sys
- (b) use #define MAX_FD 257 in close.c
- This will allow a program access to 255-3 = 252 simultaneously open files.
- (Note: To use DOS service 0x67 you need DOS 3.3 or greater).
-
-
- Notes:
- (1) Entries 0, 1, and 2 need not actually exist in the file_struct_table
- since they refer to devices rather than files (I don't know how this
- table is constructed internally to MSDOS).
-
-
-
- ******** Double dangerous curves here (TeX jargon: means BEWARE!) ***********
-
- It is possible to use int 0x21, service 0x67 to get more file handles,
- but with certain restrictions.
-
- When a program is loaded by MSDOS, it gets two blocks of memory:
-
- block 1 +--------------+
- | environment |
- +--------------+
-
- block 2 +--------------+ lower memory addresses
- | program code |
- | and data |
- | |
- +--------------+ higher memory addresses
-
- then when a request is made to MSDOS via service 0x67 for more than
- 20 handles, and extra block of memory is allocated for the new table,
- usually just above block 2:
-
- block 1 +--------------+
- | environment |
- +--------------+
-
- block 2 +--------------+ lower memory addresses
- | program code |
- | and data |
- | |
- +--------------+ higher memory addresses
- block 3 +--------------+
- | new handle |
- | table |
- +--------------+
-
- This should not necessarily cause any problems.
-
- But with Turbo C, it does! All turbo C models except the TINY model
- give access to the far heap (which is the same as the ordinary heap
- in the COMPACT, LARGE, and HUGE models). Turbo C allocates memory on
- the far heap by requesting MSDOS to increase the size of its code/data
- memory block (block 2). If block 3 has already been allocated as above
- then there is no room to increase the size of block 2, hence the
- request must fail. Once MSDOS service 0x67 has allocated block 3 then
- subsequent memory allocation calls in Turbo C which require extra
- space on the far heap will fail. This includes calls to fopen() in
- the large-data models; fopen() calls setvbuf() which in turn calls
- malloc().
-
- Therefore to use service 0x67 it will often be necessary to make
- DOS put the new handle table as high in memory as possible. Two ways
- of doing this suggest themselves:
- (i) Request DOS to increase the size of the block of memory which is
- allocated to the program, then use service 0x67, and finally request
- DOS to reduce the program's memory back to its original size. This is
- a little messy, but it will work.
- (ii) Request DOS to use the LAST FIT memory allocation strategy, then
- use service 0x67. DOS will then place the handle table as high in
- DOS memory as is possible. DOS should then be requested to use the
- original memory allocation strategy again. Code to perform this
- is included as an alternative in the file "close.c".
-
- There are potential problems with the use of schemes which put the
- handle table high in DOS memory. Firstly, it can result in some memory
- fragmentation if a high-loading tsr is loaded by a program which uses
- such a scheme. Secondly, it is more likely to cause obscure bugs or
- system crashes due to programs (or tsrs) which have previously undetected
- bugs in the way in which they use memory.
-
- Perhaps better solution is to not use MSDOS service 0x67 at all. It
- is fairly easy to perform the functions of this service but use
- space obtained from Turbo C for the handle table. In this way the
- extra block (block 3) is never created for the program. This is the
- default method used in "close.c".
-
- There is no problem with using MSDOS service 0x67 under the default
- FIRST FIT memory allocation strategy in Turbo C programs which do not
- access the far heap. This includes go32.exe of DJGPP.
-
-
- ***************** Triple dangerous curves here. *******************
-
- It appears that some extensions to MS-DOS (such as network drivers)
- make use of files (or file handles?) which have the ms bit (bit 7) set.
- On systems which have such extensions installed, it may be prudent to
- not set the files=nn statement to greater than 127.
-
-
- -------------
- 30th March 1992
-
- W. Metzenthen
- apm233m@vaxc.cc.monash.edu.au
-